import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class QueueBasedScheduler3 implements Scheduler {
	double V = 0;
	int bandwidth = 0;
	int time_slot = 1;
	double power = 0;
	int slot_number = 100000;

	private int group_number = 3;
	private Group[] group_list;
	private BufferedReader signal_reader;

	private double energy_consumption = 0;
	private int totally_delay = 0;
	private double signal_strength = 0;

	private double metric_considering_app_char = 0;
	private int queue = 0;

	Recorder recorder;

	private double overdue = 0;
	private double queue_overdue = 0;

	public int data = 0;
	private int send_data = 0;

	int queue_delay = 0;
	double queue_performance_metric = 0;

	public QueueBasedScheduler3() {

		group_list = new Group[group_number];
		recorder = new Recorder("-queue3");
		
		// 第一个参数为deadline,第二个参数为上层应用每个时隙发送数据量的平均值（poisson分布）
		group_list[0] = new Group(10, 60, 0,4,recorder);
		group_list[1] = new Group(200, 60, 1,2,recorder);
		group_list[2] = new Group(5000, 60, 2,1,recorder);

		try {
			signal_reader = new BufferedReader(new FileReader(new File("C:\\Users\\LZQ\\Desktop\\Lypunov_simulation_data\\signal_vector.csv")));
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}


	}

	@Override
	public void begin_scheduling(int slot_number) {
		// TODO Auto-generated method stub
		this.slot_number = slot_number;
		for (int t = 0; t < slot_number; t += time_slot) {
			// System.out.println("Time slot: " + t);
			// 读取信号强度，获得带宽，功率等参数
			try {
				signal_strength = Double.parseDouble(signal_reader.readLine());
			} catch (NumberFormatException | IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			V = getV();
			power = getPower();
			bandwidth = getBandwidth();

			// System.err.println("slot: "+t+" V: "+V+" Signal Strength: "+signal_strength+" Power: "+power+" BandWidth: "+bandwidth+" .");
			// 更新队长
			queue = 0;
			for (Group gp : group_list) {
				gp.refreshState(t);
				queue += gp.queue_length_sum;
			}
			// 计算决策变量
			double D = queue * bandwidth - V * power;
//			recorder.recordD(D);
			// Recorder.recordlog(new StringBuffer("#############current queue length: "+queue+" byte. \n"));
//			recorder.recordlog(new StringBuffer("————current parameters: \n Q= " + queue + " \n u= " + bandwidth + " \n V= " + V + " \n power= "
//								+ power + " \n"));
//			recorder.recordlog(new StringBuffer("————D: Q*u-V*power=" + D + "—————\n"));
			if (D > 0) {
				// 发送决策
				int allocated_bandwidth = 0;
				send_data = 0;// 统计当前时隙内发送的数据量
				while (allocated_bandwidth < bandwidth) {
					// 每一次找出最早到达的那个数据单元进行发送
					int arr_time = Integer.MAX_VALUE;
					DataUnit earliest_du = null;
					Group earliest_gp = null;
					for (int i = 0; i < group_number; i++)
						for (DataUnit du : group_list[i].DataQueue) {
							if (du.arr_time <= arr_time) {
								earliest_du = du;
								earliest_gp = group_list[i];
								arr_time = du.arr_time;
							}
						}
					// arr_time未被修改，说明队列中没有数据单元_
					if (arr_time == Integer.MAX_VALUE)
						break;
					// System.err.println("slot: "+t+"energy addition: "+power+" J.");
					if (earliest_du.data.length > (bandwidth - allocated_bandwidth)) {
						// 如果剩下的可分配的带宽不足以发送这个数据单元中的数据了
						// 实际发送的数据量为：bandwidth-allocated_bandwidth
						earliest_du.data = new byte[earliest_du.data.length - (bandwidth - allocated_bandwidth)];
						earliest_gp.queue_length[earliest_du.delay_level] -= (bandwidth - allocated_bandwidth);
						send_data += bandwidth - allocated_bandwidth;
						if ((t - earliest_du.arr_time) > earliest_du.bound) {
							overdue+=(bandwidth - allocated_bandwidth) ;
							//metric_considering_app_char += (bandwidth - allocated_bandwidth) * (t - earliest_du.arr_time) * earliest_gp.max_penalty_factor;
					
						} else {
							metric_considering_app_char += (bandwidth - allocated_bandwidth) * (t - earliest_du.arr_time) * earliest_gp.omega;
						}
						totally_delay += (bandwidth - allocated_bandwidth) * (t - earliest_du.arr_time);
						allocated_bandwidth = bandwidth;
//						recorder.recordlog(new StringBuffer("###########send " + (bandwidth - allocated_bandwidth) + " bytes data.  cause delay: "
//											+ ((bandwidth - allocated_bandwidth) * (t - earliest_du.arr_time)) + "\n"));
					} else {
						// 如果剩下的带宽足以将整个数据单元的数据发送完毕
						earliest_gp.queue_length[earliest_du.delay_level] -= earliest_du.data.length;
						allocated_bandwidth += earliest_du.data.length;
//						recorder.recordlog(new StringBuffer("###########send " + (earliest_du.data.length) + " bytes data.cause delay: "
//											+ (earliest_du.data.length * (t - earliest_du.arr_time)) + "\n"));
						send_data += earliest_du.data.length;
						if ((t - earliest_du.arr_time) > earliest_du.bound) {
//							metric_considering_app_char += earliest_du.data.length * (t - earliest_du.arr_time) * earliest_gp.max_penalty_factor;
							overdue+= earliest_du.data.length ;
						} else {
							metric_considering_app_char += earliest_du.data.length * (t - earliest_du.arr_time) * earliest_gp.omega;
						}
						totally_delay += earliest_du.data.length * (t - earliest_du.arr_time);
						// 判断这个数据单元是否超过了deadline
//						if ((t - earliest_du.arr_time) > earliest_du.bound)
//							overdue++;
						// 数据单元发送完毕，从队列中移除
						earliest_gp.DataQueue.remove(earliest_du);

					}
				}
				if (allocated_bandwidth > 0)// 如果分配了带宽，那么说明有数据发送了，能耗增加
					energy_consumption += power;
				data += send_data;
			} else {
				// 不发送，nothing to do
			}

			// 计算决策之后的队长
			queue = 0;
			for (Group gp : group_list) {
				for (int q : gp.queue_length)
					queue += q;
			}

			// 上面计算的是由发送数据包产生的时延和性能损失
			// 计算缓存在队列中的所有数据单元的时延、性能损失
			queue_delay = 0;
			queue_performance_metric = 0;
			int queue_data = 0;
			queue_overdue = 0;
			for (int i = 0; i < group_number; i++) {
				for (DataUnit du : group_list[i].DataQueue) {
					queue_delay += (t - du.arr_time) * du.data.length;
					if ((t - du.arr_time) > du.bound) {
						//queue_performance_metric += du.delay * group_list[i].max_penalty_factor * du.data.length;
						queue_overdue+=du.data.length;
					} else {
						queue_performance_metric += (t-du.arr_time) * group_list[i].omega * du.data.length;
					}

					queue_data += du.data.length;
//					if ((t - du.arr_time) > du.bound)
//						queue_overdue++;
				}
			}
//			recorder.recordlog(new StringBuffer("there are " + queue_overdue + " data unit in the queue pass its deadline.\n"));
//			recorder.recordQueueLength(queue);
//			recorder.recordEnergy(energy_consumption);
//			recorder.recordDelay(totally_delay + queue_delay);
//			recorder.recordMetric(metric_considering_app_char + queue_performance_metric);
//			recorder.recordDelayPerByte(1.0 * (totally_delay + queue_delay) / (data + queue_data));
//			recorder.recordSendData(data);
//			recorder.recordOverdue(queue_overdue + overdue);
		}
		int generated_data_number = 0;
		for (int i = 0; i < group_number; i++)
			generated_data_number += group_list[i].app.generated_data_size;

		System.out.println("Energy Consumption: " + (int) energy_consumption + " J");
		System.out.println("Totally dealy: " + (int) (totally_delay + queue_delay) + " s*byte");
		System.out.println("Totally metric considering app: " + (metric_considering_app_char + queue_performance_metric) + " s*byte*(1/omega)");
		System.out.println("Totally send data: " + (int) data + " byte");
		System.out.println("Current Queue Length: " + queue);
		System.out.println("Generated Data Size: " + generated_data_number);
		System.out.println("Overdue number: " + (overdue + queue_overdue));
		System.out.println("Delay per byte: "+(1.0*(totally_delay + queue_delay)/generated_data_number));
		System.out.println("V factor: " + V + "\n");
		recorder.recordV_GP_BP(""+V+","+(metric_considering_app_char + queue_performance_metric)+","+(overdue + queue_overdue)+","+energy_consumption);
		recorder.flush();
	}

	public double getV() {
		return V;

	}

	public double getPower() {
		return -25 * signal_strength - 1025;

	}

	public int getBandwidth() {
		return (int) ((0.909 * signal_strength + 153.63) * 3);
	}
	public void setV(double v)
	{
		V=v;
	}
}
